Skip to main content

第 5 章:透過多種方式調度 Pods

Controllers 的定義與種類

追蹤至少一種 Kubernetes 資源類型。 這些物件有一個代表所需狀態的 spec 欄位。 此資源的 controller 負責使當前狀態更接近所需狀態

  • ReplicaSet
  • Deployment
  • DaemonSet
  • StatefulSet
  • Job
  • CronJob

透過 Replication Controller 擴展 Pod

什麼是 Replication Controller

  • Replication Controller 就是 Kubernetes上用來管理 Pod 的數量以及狀態的 controller,我們可以用 Replication Controller 在 Kubernetes 上做到以下幾件事:
    • 每個 Replication Controller 都有屬於自己的 yaml 檔
    • 在Replication Controller 設定檔中 可以指定同時有多少個相同的 Pods運行在 Kubernetes Cluster上
    • 當某一 Pod 發生 crash, failed,而終止運行時,Replication Controller 會幫我們自動偵測,並且自動創建一個新的Pod,確保 Pod 運行的數量與設定檔的指定的數量相同
    • 當機器重新開啟時,之前在機器上運行的 Replication Controller 會自動被建立,確保pod隨時都在運行。
my-replication-controller.yaml
apiVersion: v1
kind: ReplicationController
metadata:
name: my-replication-controller
spec:
replicas: 3
selector:
app: hello-pod-v1
template:
metadata:
labels:
app: hello-pod-v1
spec:
containers:
- name: my-pod
image: nginx
ports:
- containerPort: 3000
  • spec.replicas & spec.selector
    • spec.replicas 中,我們必須定義Pod 的數量,以及在 spec.selector 中指定我們要選擇的Pod的條件(labels)。
  • spec.template
    • 在spec.template中我們會去定義pod的資訊,包含Pod的labels以及Pod中要運行的container。
  • spec.template.metadata
    • 則是Pod的labels,metadata.labels必須被包含在select中,否則在創建Replication Controller物件時,會發生error。
  • spec.template.spec
    • 最後spec的部分則是定義container,可以參考 Pod的yaml檔 的範例,在我們的範例中,一個Pod只有一個container。

總結以上,可以知道在 my-replication-controller.yaml 中我們想創建一個Replication Controller物件,來確保擁有"app=hello-pod-v1"label 的 Pod,在任何時候,Kubernetes Cluster 中的數量都為 3

管理 Replication Controller

在建立Replication Controller之前,我們先檢查一下minikube狀態是否Ready

kubectl get nodes
NAME       STATUS    ROLES     AGE       VERSION
minikube Ready <none> 2d v1.8.0

確認沒問題之後,可以透過kubectl create的指令,創建一個新的Replication Controller物件

kubectl create -f my-replication-controller.yaml
replicationcontroller/my-replication-controller created

當新增replication controller成功之後,我們可以使用kubectl get rc查看目前狀態

kubectl get rc
NAME                        DESIRED   CURRENT   READY   AGE
my-replication-controller 3 3 3 20s

從圖中可以知道,my-replication-controller 這個物件目前管理 3 個Pod,且3個Pod的狀態皆為Ready,這時我們在查看一下Pod的狀態

kubectl get pods
NAME                              READY   STATUS    RESTARTS   AGE
my-replication-controller-gx9ft 1/1 Running 0 85s
my-replication-controller-j9c9p 1/1 Running 0 85s
my-replication-controller-mrb26 1/1 Running 0 85s

可以發現在Kubernetes Cluster中,replication controller自動幫我們創建好三個pod,且這三個pod是名稱是unique的。如果我們用kubectl describe指令去查看其中一個pod的詳細資料

kubectl describe pod my-replication-controller-gx9ft
Name:             my-replication-controller-gx9ft
Namespace: default
Priority: 0
Service Account: default
Node: k8s-wrk-1/10.0.1.238
Start Time: Mon, 08 Jan 2024 13:18:29 +0000
Labels: app=hello-pod-v1
Annotations: <none>
Status: Running
IP: 172.16.2.3
IPs:
IP: 172.16.2.3
Controlled By: ReplicationController/my-replication-controller
Containers:
my-pod:
Container ID: containerd://4f4bc34f83e816510995beb9bb307f3a1bd7c248eced42ce1686e571ec4f14de
Image: nginx
Image ID: docker.io/library/nginx@sha256:2bdc49f2f8ae8d8dc50ed00f2ee56d00385c6f8bc8a8b320d0a294d9e3b49026
Port: 3000/TCP
Host Port: 0/TCP
State: Running
Started: Mon, 08 Jan 2024 13:18:38 +0000
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-6vfwx (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
kube-api-access-6vfwx:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 2m15s default-scheduler Successfully assigned default/my-replication-controller-gx9ft to k8s-wrk-1
Normal Pulling 2m14s kubelet Pulling image "nginx"
Normal Pulled 2m6s kubelet Successfully pulled image "nginx" in 1.256s (8.46s including waiting)
Normal Created 2m6s kubelet Created container my-pod
Normal Started 2m6s kubelet Started container my-pod

可以看到這個pod是由my-replication-controller物件創建的。如果這時我們手動刪除其中一個Pod,我們來看看會發生什麼事情

kubectl delete pod my-replication-controller-gx9ft
pod "my-replication-controller-gx9ft" deleted
  • 會另外產生一個 Pod
NAME                              READY   STATUS              RESTARTS   AGE
my-replication-controller-mrb26 1/1 Running 0 4m39s
my-replication-controller-n9b5v 1/1 Running 0 68s
my-replication-controller-z4w4k 0/1 ContainerCreating 0 2s
  • 數秒之間,Pod 會瞬間 ready
NAME                              READY   STATUS    RESTARTS   AGE
my-replication-controller-mrb26 1/1 Running 0 4m51s
my-replication-controller-n9b5v 1/1 Running 0 80s
my-replication-controller-z4w4k 1/1 Running 0 14s

可以看到replication controller偵測到一個Pod終止服務時,產生另外一個新的Pod,來確保Pod的數量。

我們也可以透過kubectl scale來scaling Pod的數量,指令如下

kubectl scale --replicas=4 -f my-replication-controller.yaml
replicationcontroller/my-replication-controller scaled

這時候再去查看kubectl get rc或是kubectl get pods,可以發現Pod的數量多一個了。我們也可以用同樣的方式去縮減Pod的數量

kubectl get rc
NAME                        DESIRED   CURRENT   READY   AGE
my-replication-controller 4 4 4 14m

也可以透過kubectl describe去看目前replication controller的詳細資料,更多log資訊可參考 kubectl-describe-rc.log

kubectl describe rc my-replication-controller
NAME                        DESIRED   CURRENT   READY   AGE
my-replication-controller 4 4 4 14m
root@k8s-msr-1:/home/ubuntu# kubectl describe rc my-replication-controller
Name: my-replication-controller
Namespace: default
Selector: app=hello-pod-v1
Labels: app=hello-pod-v1
Annotations: <none>
Replicas: 4 current / 4 desired
Pods Status: 4 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
Labels: app=hello-pod-v1
Containers:
my-pod:
Image: nginx
Port: 3000/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 14m replication-controller Created pod: my-replication-controller-j9c9p
Normal SuccessfulCreate 14m replication-controller Created pod: my-replication-controller-mrb26
Normal SuccessfulCreate 14m replication-controller Created pod: my-replication-controller-gx9ft
Normal SuccessfulCreate 10m replication-controller Created pod: my-replication-controller-n9b5v
Normal SuccessfulCreate 9m52s replication-controller Created pod: my-replication-controller-z4w4k
Normal SuccessfulCreate 69s replication-controller Created pod: my-replication-controller-8ftts

最後,當我們刪除 replication controller時,要特別注意,由 replication controller 產生的pod也會因此而終止服務

kubectl delete rc my-replication-controller
replicationcontroller "my-replication-controller" deleted

如果你希望刪掉 replication controller 之後,這些 Pod 仍然運行,可以指定 --cascade=false,指令如下

kubectl delete rc my-replication-controller--cascade=false

如果 Pod 的數量很大,將會花一些時間來完成整個刪除動作,可以使用指令強迫 pod 立即結束

kubectl delete pods $POD--grace-period=0 --force

更多關於kubectl delete 的指令應用,可以參考 Kubernetes 官網文件,裡面有詳細介紹,有興趣的讀者不妨看一看

Deployment 建立多個相同的 Pod

Deployment 可以視為一組相同 Pod 所組成的服務管理單元,其中可以透過副本 (Replicas) 參數指定所需的 Pod 數量。Deployment 可協助確保應用程式可以有一或多個執行個體處理使用者要求並會自動取代失敗或已從節點中移除的 Pod。以下圖為例,該 Deployment 內指定 K8S Cluster 需建置兩組相同服務的 Pod,而此兩組 Pod 會由 Cluster 布建於 node 之中,且 K8S 會於系統內持續維持兩組運行中的 Pod 來提供服務讓用戶端存取,

Deployment 創建後,會同步創建

  • ReplcaSet
    • 可以當成 Replication Controller,Replica Sets 提供了 更彈性的 selector
  • Pod

deployment.png

使用 deployment 的好處

  • Pod Failures: 建立新的 pod
  • Node Failures
    • kube-contorller-manager 有一個 timeout 的設置 pod-eviction-timeout (預設 5 min)
    • Node 如果失聯超過 5 分鐘,就會觸發在其上運行的 Pod 的終止和重建
  • 無停機的系統升級(Zero Downtime Rollout)
    • 當 Pod 需要更新時,會先建立新的 Pod,該 Pod 開始正常運行後,才會刪除舊的 Pod

Deployment 建立與刪除

  • 命令式建立
kubectl create deployment web --image=nginx:1.14.2
kubectl scale deployment web --replicas=5
  • 宣告式建立
kubectl apply -f nginx.yaml
deployment.apps/web created

kubectl get pods
NAME READY STATUS RESTARTS AGE
web-685666b9cc-62tjd 1/1 Running 0 24s
web-685666b9cc-krpwr 1/1 Running 0 24s
web-685666b9cc-tlzhd 1/1 Running 0 24s
nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: web
name: web
spec:
replicas:
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- image: nginx:1.14.2
name: nginx
  • replicas
    • 指定要建立多少個相同的 Pod,當運行的 Pod 數量低於此數字,會自動幫我們增加 Pod
  • template
    • 指定這個 Deployment 所建立的 Pod 的設定,重建後的 Pod 也依此設定重建
    • 內容與 Pod YAML 內 metadata 和 spec 的欄位相同
  • selector
    • 指定這個 Deployment 的規則要適用到哪些 Pod,可依據 Pod 的 label 來選取
  • 刪除 Delpoyment
kubectl delete deployment web
deployment.apps "web" deleted

查看 Deployment 與 ReplicaSet 狀態

kubectl get deployment
NAME   READY   UP-TO-DATE   AVAILABLE   AGE
web 3/3 3 3 48s
kubectl get rs
NAME             DESIRED   CURRENT   READY   AGE
web-685666b9cc 3 3 3 39s

取得 Deployment / Replication Set / Pod 基本資訊

kubectl config view
kubectl get all
NAME                       READY   STATUS      RESTARTS   AGE
pod/web-685666b9cc-25xb4 1/1 Running 0 3m30s
pod/web-685666b9cc-5zs9l 1/1 Running 0 3m30s
pod/web-685666b9cc-zkxf4 1/1 Running 0 3m30s

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4h28m

NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/web 3/3 3 3 3m30s

NAME DESIRED CURRENT READY AGE
replicaset.apps/web-685666b9cc 3 3 3 3m30s

NAME COMPLETIONS DURATION AGE
job.batch/my-job 1/1 57s 86m
root@k8s-msr-1:/home/ubuntu#

在 Deployment 上系統更新

  1. 更新策略 :RollingUpdate 

創建一個新的 replicaSet 並建立新的 pod,刪除舊的 replicaSet 並刪除舊的 pod

  • 創建一個 deployment
kubectl create deployment web --image=nginx:1.14 --replicas 3
  • 更新 Image
kubectl set image deployment/web nginx=nginx:1.14.2
  • 系統升級

    查看系統升級歷史

    kubectl rollout history deployment web

    查看系統升級歷史版本

    kubectl rollout history deployment web --revision=1

    執行系統升級 rollout

    kubectl rollout undo deployment web --to-revision=1
  1. 更新策略 :Recreate

終止目前 ReplicaSet 中的所有 Pod,在擴充新 ReplicaSet 之前設定(當應用程式不支援同時運行不同版本時使用)

  • 重啟 deployment
kubectl rollout restart deployment hello-world

透過 YAML 更新 Deployment

kubectl edit deploy $DEPLOYMENT
my-replication-controller.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "1"
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"labels":{"app":"web"},"name":"web","namespace":"default"},"spec":{"replicas":3,"selector":{"matchLabels":{"app":"web"}},"template":{"metadata":{"labels":{"app":"web"}},"spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx"}]}}}}
creationTimestamp: "2024-01-09T07:56:34Z"
generation: 2
labels:
app: web
name: web
namespace: default
resourceVersion: "25637"
uid: 246ed67e-f59b-4b97-bda2-77d2ac2e3b4d
spec:
progressDeadlineSeconds: 600
replicas: 5
revisionHistoryLimit: 10
selector:
matchLabels:
app: web
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
app: web
spec:
containers:
- image: nginx:1.14.2
imagePullPolicy: IfNotPresent
name: nginx
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst

擴展 Deployment

kubectl scale deployment web --replicas 5

StatefulSet

StatefulSet 是用來管理有狀態應用的工作負載 API 對象。

StatefulSet 用來管理某 Pod 集合的部署和擴縮, 並為這些 Pod 提供持久存儲和持久標識符。

和 Deployment 類似, StatefulSet 管理基於相同容器規約的一組 Pod。但和 Deployment 不同的是, StatefulSet 為它們的每個 Pod 維護了一個有粘性的 ID。這些 Pod 是基於相同的規約來創建的, 但是不能相互替換:無論怎麽調度,每個 Pod 都有一個永久不變的 ID。

如果希望使用存儲卷為工作負載提供持久存儲,可以使用 StatefulSet 作為解決方案的一部分。 盡管 StatefulSet 中的單個 Pod 仍可能出現故障, 但持久的 Pod 標識符使得將現有卷與替換已失敗 Pod 的新 Pod 相匹配變得更加容易。

使用 StatefulSet

StatefulSet 對於需要滿足以下一個或多個需求的應用程序很有價值:

穩定的、唯一的網絡標識符。 穩定的、持久的存儲。 有序的、優雅的部署和擴縮。 有序的、自動的滾動更新。

StatefulSet 限制

給定 Pod 的存儲必須由 PersistentVolume Provisioner 基於所請求的 storage class 來制備,或者由管理員預先制備。 刪除或者擴縮 StatefulSet 並不會刪除它關聯的存儲卷。 這樣做是為了保證數據安全,它通常比自動清除 StatefulSet 所有相關的資源更有價值。 StatefulSet 當前需要無頭服務來負責 Pod 的網絡標識。你需要負責創建此服務。 當刪除一個 StatefulSet 時,該 StatefulSet 不提供任何終止 Pod 的保證。 為了實現 StatefulSet 中的 Pod 可以有序且體面地終止,可以在刪除之前將 StatefulSet 縮容到 0。 在默認 Pod 管理策略(OrderedReady) 時使用滾動更新, 可能進入需要人工幹預才能修覆的損壞狀態。

StatefulSet 範本

apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx # 必须匹配 .spec.template.metadata.labels
serviceName: "nginx"
replicas: 3 # 默认值是 1
minReadySeconds: 10 # 默认值是 0
template:
metadata:
labels:
app: nginx # 必须匹配 .spec.selector.matchLabels
spec:
terminationGracePeriodSeconds: 10
containers:
- name: nginx
image: registry.k8s.io/nginx-slim:0.8
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "my-storage-class"
resources:
requests:
storage: 1Gi
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx

DaemonSet 背景運作 Pod

確保所有或部分 Kubernetes 叢集節點上執行一個 pod。 當有新節點加入時,pod 也會運行在上面。常見例子:

  • kube-proxy 網路相關
  • log collectors
  • metric servers
  • Resource monitoring agent
  • storage daemons

DamemonSet 範本

apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd-elasticsearch
namespace: kube-system
labels:
k8s-app: fluentd-logging
spec:
selector:
matchLabels:
name: fluentd-elasticsearch
template:
metadata:
labels:
name: fluentd-elasticsearch
spec:
tolerations:
# 这些容忍度设置是为了让该守护进程集在控制平面节点上运行
# 如果你不希望自己的控制平面节点运行 Pod,可以删除它们
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
containers:
- name: fluentd-elasticsearch
image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
# 可能需要设置较高的优先级类以确保 DaemonSet Pod 可以抢占正在运行的 Pod
# priorityClassName: important
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log

Job 任務:一次性運行的 Pod

一般為執行某個指令或腳本,執行結束後 Pod 的生命隨之結束

kubectl create job my-job --image=busybox -- sh -c "sleep 50"
NAME           READY   STATUS    RESTARTS   AGE
my-job-5jxkj 1/1 Running 0 10s
job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: my-job
spec:
template:
spec:
containers:
- name: my-job
image: busybox
command: ["sh", "-c", "sleep 50"]
restartPolicy: Neve

CronJob 計劃任務:排程時間運行的 Pod

cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: hello
spec:
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox:1.28
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailurapiVersion: batch/v1
kind: Job
metadata:
name: my-job
spec:
template:
spec:
containers:
- name: my-job
image: busybox
command: ["sh", "-c", "sleep 50"]
restartPolicy: Never

官方參考連結